Skip to content

Allow explicit wildcard GitHub App repositories in strict mode - #66039

Merged
pelikhan merged 5 commits into
mainfrom
copilot/v0903-fix-github-app-repositories
Oct 6, 2026
Merged

pelikhan merged 5 commits into
mainfrom
copilot/v0903-fix-github-app-repositories

Conversation

Copilot AI commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Strict mode rejects GitHub App tokens configured with repositories: ["*"] because the compiler correctly omits the action’s repositories input. This blocks documented cross-repository access for GitHub tools and safe outputs.

  • Scope validation: Recognize compiler-generated token steps backed by an explicit wildcard configuration without adding a repositories input or suggesting a current-repository restriction.
  • Permission safeguards: Continue requiring explicit permission-* inputs and repository scoping for unrelated token steps.
  • Regression coverage: Cover both GitHub tool and safe-output wildcard configurations in strict mode.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix strict mode rejection for github-app.repositories Allow explicit wildcard GitHub App repositories in strict mode Oct 6, 2026
Copilot AI requested a review from pelikhan October 6, 2026 06:10
@pelikhan
pelikhan marked this pull request as ready for review October 6, 2026 06:11
Copilot AI balanced review requested due to automatic review settings October 6, 2026 06:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Workflow-global matching can incorrectly exempt an unrelated custom token step with matching ID and credentials.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Allows strict-mode compilation for explicitly wildcard-scoped GitHub App tokens.

Changes:

  • Tracks compiler-generated wildcard token steps.
  • Preserves permission validation while exempting wildcard steps from repository-input validation.
  • Adds GitHub tool and safe-output regression tests.
File Description
pkg/​workflow/​safe_outputs_app_config.go Records generated wildcard token steps.
pkg/​workflow/​compiler.go Resets wildcard tracking per compilation.
pkg/​workflow/​compiler_types.go Adds compiler tracking state.
pkg/​workflow/​app_token_permissions_validation.go Recognizes tracked wildcard steps during validation.
pkg/​workflow/​app_token_permissions_validation_test.go Covers wildcard compilation and permission safeguards.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +28 to +29
return id != "" && clientID != "" && privateKey != "" &&
c.wildcardAppTokenSteps[appTokenStepKey{id, clientID, privateKey}]

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bac8c5f: wildcard exemptions are now scoped to the containing job and matched step metadata, with only one matching step allowed to consume the exemption. Regression tests cover same-job and cross-job spoofing.

@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

✅ Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch test file diff

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

✅ Ponytail Reviewer completed successfully!

Lean already. Ship.

Generated by Ponytail Reviewer for #66039

@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

✅ PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

✅ Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft.

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate: ADR Required

This PR triggered ADR enforcement and no Architecture Decision Record was found.

Why enforcement applies

  • implementation label: not present
  • Business-logic additions: 111 new lines across pkg/ (threshold: 100) in 5 files
  • No .design-gate.yml override found — defaults used

ADR search results

Draft ADR committed

I generated a draft and pushed it to this branch:

docs/adr/66039-trust-compiler-generated-wildcard-app-token-steps.md

Decision captured: the compiler records, at generation time, which actions/create-github-app-token steps it emitted from an explicit repositories: ["*"] configuration (Compiler.wildcardAppTokenSteps, keyed by step id + client-id + private-key), and strict-mode validation consults that record before flagging a missing repositories input.

Evidence used

  • pkg/workflow/compiler_types.go — new wildcardAppTokenSteps map[appTokenStepKey]bool field
  • pkg/workflow/safe_outputs_app_config.go — records wildcard steps in buildGitHubAppTokenMintStepWithMeta
  • pkg/workflow/app_token_permissions_validation.go — hasGeneratedWildcardAppTokenStep short-circuits the repositories check
  • pkg/workflow/compiler.go — per-compilation reset of the map
  • pkg/workflow/app_token_permissions_validation_test.go — regression coverage for github-tool and safe-outputs wildcards, plus negative cases

Alternatives documented: emitting a literal repositories: "*" input, threading the flag through WorkflowData, and blanket-exempting generated step IDs.

Next action

Review docs/adr/66039-trust-compiler-generated-wildcard-app-token-steps.md, correct anything I inferred wrongly (especially Deciders and the negative consequences), and change Status from Draft to Proposed/Accepted before merge.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · pi · opus50 · 35.2 AIC · ⌖ 50.3 AIC · ⊞ 1.7K · ◷
Comment /review to run again

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Comment Memory

Peek at saved memory (pr-code-quality-reviewer)
reviewed_at: 2026-10-06T06:27:38Z
review_event: REQUEST_CHANGES
top_themes:
  - wildcard app-token exemption can leak across steps because the matching key is not provenance-safe
  - regression coverage still misses the fingerprint-collision case
files_reviewed:
  - pkg/workflow/app_token_permissions_validation.go
  - pkg/workflow/app_token_permissions_validation_test.go
  - pkg/workflow/compiler.go
  - pkg/workflow/compiler_types.go
  - pkg/workflow/safe_outputs_app_config.go
comment_count: 0

Note

This comment is managed by comment memory.

Expand the saved memory block to view or edit the persistent context for this thread.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 36.6 AIC · ⌖ 5.51 AIC · ⊞ 21.1K · ◷
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes

The wildcard-app-token exemption still looks unsafe: it is keyed only by step ID plus the raw client-id/private-key strings, so another step in the same workflow can inherit the exemption without ever coming from an explicit repositories: ["*"] config.

Blocking theme

Step IDs are only job-local, and the validator never records job identity or provenance. That means a second actions/create-github-app-token step that reuses the same generated tuple can bypass the repository-scoping check entirely. Strict mode is supposed to reject exactly that class of unscoped token, so this change weakens the guardrail instead of narrowing it.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 36.6 AIC · ⌖ 5.51 AIC · ⊞ 21.1K
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds a narrow, well-intentioned exemption so explicitly configured wildcard (repositories: ["*"]) GitHub App tokens don't trip the strict-mode "missing repositories input" check. Test coverage for the happy path is good.

However, there's a blocking correctness/security issue: the exemption (hasGeneratedWildcardAppTokenStep) identifies a "compiler-generated" step only by matching its visible id/client-id/private-key fields, which are all values a workflow author can freely reproduce in an unrelated, hand-authored step. This lets any custom step spoof the exemption and skip the repositories scoping requirement entirely — exactly the bypass the safeguard is meant to prevent. See inline comment for details and suggested fix (track provenance of the generated step object/position rather than matching on content).

Scope of review

This is a backend Go compiler/validation change with no UI surface, so the Impeccable UI review modes (audit/critique/harden/distill/extract/clarify) don't apply. Reviewed directly for correctness and security per the fallback guidance.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · copilot · sonnet50 · 46.4 AIC · ⌖ 13.4 AIC · ⊞ 8.2K

return false
}
return id != "" && clientID != "" && privateKey != "" &&
c.wildcardAppTokenSteps[appTokenStepKey{id, clientID, privateKey}]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed: the wildcard exemption can be spoofed by an unrelated, custom step.

hasGeneratedWildcardAppTokenStep matches purely on step["id"], with["client-id"], and with["private-key"] — all of which are attacker/author-controlled strings in the compiled workflow, not a reference to the actual generated step object. Any hand-written job (e.g. one added via an import, or directly in the markdown's custom steps:) that reuses the same id: github-mcp-app-token (or safe-outputs-app-token) together with the same client-id/private-key expressions will be misclassified as compiler-generated and skip the repositories requirement in strict mode — even though it has no connection to the actual wildcard-configured GitHub App step.

This defeats the stated safeguard: "Continue requiring explicit repositories input... for unrelated token steps" (see PR description). Since client-id/private-key are typically ${{ vars.APP_ID }} / ${{ secrets.APP_KEY }} — values an author of the workflow already knows/controls — this is trivially copyable, not a secret binding.

Suggested fix: track provenance via an identity that user-authored steps can't fabricate, e.g.:

  • Record the (jobName, step index) or a pointer/marker written into the step map at generation time (e.g. an internal field not emitted to YAML, consulted before stripping), rather than matching on step content, or
  • Validate/tag compiler-generated steps before they are merged with custom/imported steps, so the exemption is based on "this step object came from the generator" rather than "this step's visible fields happen to match."

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bac8c5f: custom steps reusing the generated ID and credential expressions no longer inherit the exemption from another job; duplicate matches in the generated job are also rejected.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /codebase-design and /tdd — requesting changes on the job-scoping gap in the new wildcard exemption.

📋 Key Themes & Highlights

Key Themes

  • Collision risk in the exemption key: appTokenStepKey{id, clientID, privateKey} is built from a job-local step id but stored in a compiler-wide map (c.wildcardAppTokenSteps), spanning every job in the compiled workflow. A user-authored step in an unrelated job that coincidentally shares the same id/client-id/private-key combination as a compiler-generated wildcard step would silently inherit the exemption from strict-mode scoping checks — this was already flagged by an existing Copilot review comment and is worth fixing before merge.
  • Test coverage is narrow: the new regression tests cover only 2 of ~13 call sites of buildGitHubAppTokenMintStepWithMeta. The fix is plausibly correct for all of them, but only the github tool and safe-outputs paths are verified.

Positive Highlights

  • ✅ Clear problem statement and well-scoped change — only touches the specific hasExplicitAppTokenRepositories check path, not the broader permission check.
  • ✅ Good defense-in-depth test (TestAppTokenPermissionsWildcardStillChecksPermissionsAndOtherSteps) confirming the exemption doesn't bypass permission-* validation and doesn't leak to unrelated steps with a different client-id.
  • ✅ c.wildcardAppTokenSteps = nil reset in CompileWorkflowData correctly prevents state leaking across compiles of different workflows in batch mode.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 114.1 AIC · ⌖ 14.8 AIC · ⊞ 10.2K
Comment /matt to run again

return false
}
return id != "" && clientID != "" && privateKey != "" &&
c.wildcardAppTokenSteps[appTokenStepKey{id, clientID, privateKey}]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/codebase-design] The existing Copilot review comment on this line is correct and worth confirming: appTokenStepKey{id, clientID, privateKey}} is keyed only on step id, which is a job-local identifier, but wildcardAppTokenSteps is a single compiler-wide map built across all jobs in the workflow. Any actions/create-github-app-token step in an unrelated custom job (steps:, jobs:) that happens to reuse the same id (e.g. github-mcp-app-token) plus the same client-id/private-key expressions would be silently exempted from the repositories/permission scoping check, even if its own repositories input is missing or broader than intended.

💡 Suggested fix

Scope the key to the job as well, e.g. appTokenStepKey{jobName, id, clientID, privateKey}, and pass jobName down from validateAppTokenPermissions into hasGeneratedWildcardAppTokenStep. This keeps the exemption tied to exactly the compiler-generated step it was computed for, closing the collision window a user-authored step could otherwise exploit to bypass strict-mode scoping.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bac8c5f: the provenance key now includes the containing job, and validation only exempts one matching step. Added tests for cross-job and same-job spoofing.

Comment thread pkg/workflow/safe_outputs_app_config.go Outdated

func (c *Compiler) buildGitHubAppTokenMintStepWithMeta(app *GitHubAppConfig, permissions *Permissions, fallbackRepoExpr string, ownerSourceRepository string, stepName string, stepID string) []string {
safeOutputsAppLog.Printf("Building GitHub App token mint step: owner=%s, repos=%d", app.Owner, len(app.Repositories))
if len(app.Repositories) == 1 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] Side effect (c.wildcardAppTokenSteps[...] = true) is recorded unconditionally every time buildGitHubAppTokenMintStepWithMeta runs with repositories: ["*"], even for call sites where the generated step is never subsequently fed through validateAppTokenPermissions on the same parsedWorkflow pass (e.g. helper call sites used only to produce YAML fragments for embedding elsewhere). The tests added cover the two documented call sites (github tool, safe-outputs) but not the ~10 other callers of this function (steering issue, checkout, plugin installation, dispatch-repository, etc.) that also pass app.Repositories == ["*"].

💡 Suggested test

Add a case (or a table-driven loop) exercising at least one more caller path — e.g. tools.checkout[*].github-app or safe-outputs.dispatch-repository — with repositories: ["*"] in strict mode, to confirm the exemption generalizes correctly rather than only working for the two paths explicitly tested.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bac8c5f: the YAML-fragment builder is side-effect free; wildcard provenance is recorded at job-emission points. Strict-mode coverage now also exercises dispatch-repository safe outputs.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot address the following outstanding work in one pass:

  1. Update this branch with the latest main using make merge-main, resolving any conflicts and preserving the intended changes.
  2. Review (pkg/workflow/app_token_permissions_validation.go:29): This exemption is workflow-global, but step IDs are only job-local. If a generated wildcard step uses (github-mcp-app-token, ${{ vars.APP_ID }}, ${{ secrets.APP_KEY }}), an unrelated custom job can use the same ID and credential expressions and omit repositories; this lookup then classifies that custom step as compiler-generated and strict mode accepts it. That contradicts the stated safeguard for unrelated token steps. Track provenance with the containing job/step identity (or validate compiler-owned steps before custom steps are merged) rather than matching only these user-copyable field... - Allow explicit wildcard GitHub App repositories in strict mode #66039 (comment)
  3. Review (pkg/workflow/app_token_permissions_validation.go:29): Confirmed: the wildcard exemption can be spoofed by an unrelated, custom step. - Allow explicit wildcard GitHub App repositories in strict mode #66039 (comment)
  4. Review (pkg/workflow/app_token_permissions_validation.go:29): [/codebase-design] The existing Copilot review comment on this line is correct and worth confirming: appTokenStepKey{id, clientID, privateKey}} is keyed only on step id, which is a job-local identifier, but wildcardAppTokenSteps is a single compiler-wide map built across all jobs in the workflow. Any actions/create-github-app-token step in an unrelated custom job (steps:, jobs:) that happens to reuse the same id (e.g. github-mcp-app-token) plus the same client-id/private-key expressions would be silently exempted from the repositories/permission scoping check, even if... - Allow explicit wildcard GitHub App repositories in strict mode #66039 (comment)
  5. Review (pkg/workflow/safe_outputs_app_config.go:429): [/tdd] Side effect (c.wildcardAppTokenSteps[...] = true) is recorded unconditionally every time buildGitHubAppTokenMintStepWithMeta runs with repositories: ["*"], even for call sites where the generated step is never subsequently fed through validateAppTokenPermissions on the same parsedWorkflow pass (e.g. helper call sites used only to produce YAML fragments for embedding elsewhere). The tests added cover the two documented call sites (github tool, safe-outputs) but not the ~10 other callers of this function (steering issue, checkout, plugin installation, dispatch-repository... - Allow explicit wildcard GitHub App repositories in strict mode #66039 (comment)

Push the necessary fixes, reply to each listed review thread and resolve it when addressed. Ignore feedback already answered or resolved. Use the pr-finisher skill and stop when only human review or CI remains; do not trigger CI.

Sous-chef head: 725a850
Sous-chef work: 49f35b121a6bc17b2f2e4e3658cb17c3e2b8373b077bc58af9e5a399a80fd402 73bd99972bcf0a803af6d4bf07b91535bfb7f693fbc53a08359fe60524b41593 88a9b1ef04e3455d013f223988fc374bd23815bdf378bfbf45bf89dcc443cfc8 bb24d599490f5390ba6cda90f8d2bbb4491de1740ecbfbdffa765cd7c1e77ab2
Sous-chef state: 42f7ef2737fe2f7beacf7846a2e6adf103584907fe59a88805c10f6f4983dd9a

Generated by 👨‍🍳 PR Sous Chef · pi · haiku45 · 11.4 AIC · ⌖ 7.68 AIC · ⊞ 1K · ◷
Comment /souschef to run again

…hub-app-repositories

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Copilot AI requested a review from gh-aw-bot October 6, 2026 07:37
@pelikhan
pelikhan merged commit 869a936 into main Oct 6, 2026
2 checks passed
@pelikhan
pelikhan deleted the copilot/v0903-fix-github-app-repositories branch October 6, 2026 11:52
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.91.2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v0.90.3: strict mode rejects documented github-app.repositories: ["*"] (no explicit repositories input)

4 participants